<?php
/*-------------------------------------------------------------------------\
|
|  ========================================================
|  Fusion Registry GPL
|  Copyright (C) 2007 Fusion Scripts
|
|  This program is free software; you can redistribute it and/or
|  modify it under the terms of the GNU General Public License
|  as published by the Free Software Foundation; either version 2
|  of the License, or (at your option) any later version.
|
|  This program is distributed in the hope that it will be useful,
|  but WITHOUT ANY WARRANTY; without even the implied warranty of
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
|  GNU General Public License for more details.
|
|  You should have received a copy of the GNU General Public License
|  along with this program; if not, write to the Free Software
|  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
|  ========================================================
|
|  Email: info@fusionscripts.com
|  Web-Site: http://www.fusionscripts.com/
|
|  $Id: upload.php 4 2008-08-03 01:43:03Z registry.fusion $
|
\-------------------------------------------------------------------------*/

/**
 * Upload/Attachment related functions for the Fusion Registry.
 *
 * @package Fusion Registry
 * @subpackage lib
 * @version $Id: upload.php 4 2008-08-03 01:43:03Z registry.fusion $
 */

if ( ! defined( 'IN_IPB' ) )
{
    print "<h1>Incorrect access</h1>You cannot access this file directly. If you have recently upgraded, make sure you upgraded all the relevant files.";
    exit();
}

/**#@+
 * Fusion Registry requires
 */
require_once( KERNEL_PATH.'class_image.php' );
require_once( KERNEL_PATH.'class_upload.php' );
/**#@-*/

/**
 * Class 'registry_upload' contains attachment related functions.
 *
 * This class handles the uploading, and thumbnail generation of
 * attachments.
 *
 * @package Fusion Registry
 * @subpackage lib
 */
class registry_upload
{
    var $ipsclass;

    /** @var object */
    var $class  = "";

    /** @var string */
    var $error_msg = "";

    /**
     * Constructor, nothing too special here
     *
     * @param object $class Our parent class object
     * @return boolean TRUE
     */
    function auto_run($class)
    {
        $this->class = &$class;

        return TRUE;
    }


    /**
     * Create thumbnail
     *
     * This will handle the creation of a thumbnail for local and remote
     * files whether they are using ImageMagick or GD as their image
     * toolkit.
     *
     * @param array $data
     * @param string $type garage|registry_gallery|registry_mod
     * @param integer $id MySQL id for the type
     * @return array Array with keys 'thumb_width', 'thumb_height', 'thumb_location'
     */
    function create_thumbnail( $data, $type, $id=0 )
    {
        $return = array();

        // Initialize class_image
        $image = new class_image();

        // If it is a remote image, don't place the ./uploads/ path into our object
        if ( preg_match( "/^http:\/\/$/i", $data['attach_location'] ) )
        {
            $in_file_dir = '';
        }
        else
        {
            $in_file_dir = $this->ipsclass->vars['upload_dir'];
        }

        $image->in_type        = 'file';
        $image->out_type       = 'file';
        $image->in_file_dir    = $in_file_dir;
        $image->in_file_name   = $data['attach_location'];
        $image->desired_width  = $this->class->registry_config['thumbnail_resolution'];
        $image->desired_height = $this->class->registry_config['thumbnail_resolution'];
        $image->gd_version     = $this->ipsclass->vars['gd_version'];

        // Are we using imagemagick?
        if ( ($this->class->registry_config['thumbnail_type'] == 'imagemagick') and
             (file_exists(preg_replace("/\"/", "",$this->class->registry_config['convert_path']))) )
        {
            // Generate our temp file name and the thumb file name
            $tmp_file_name = preg_replace( "/^(.*)\.\S+$/", "\\1", $data['attach_location'] );
            $thumb_file_name = $tmp_file_name . '_thumb';

            // Append our file extension to both
            $tmp_file_name .= '.'.$data['attach_ext'];
            $thumb_file_name .= '.'.$data['attach_ext'];
        }

        // Are we dealing with a remote image?
        if ( preg_match( "/^http:\/\//i", $data['attach_location'] ) )
        {
            // Does it exist?
            if ( !$this->remote_file_exists($data['attach_location']) )
            {
                return $return;
            }

            // Create our unique file name schema here since remote images
            //   store a URL in the attach_location field, which would normally
            //   be our uniqute file name, but for remote alas, it is not ;)
            $tmp_file_name = $type ."-$id-". time();
            $thumb_file_name = $tmp_file_name . '_thumb';

            // Append our file extension to both
            $tmp_file_name .= '.'.$data['attach_ext'];
            $thumb_file_name .= '.'.$data['attach_ext'];

            // Download the remote image to our temporary file
            $infile = @fopen ($data['attach_location'], "rb");
            $outfile = @fopen ($this->ipsclass->vars['upload_dir']."/".$tmp_file_name, "wb");

            // Set our custom timeout
            if ( function_exists('stream_set_timeout') )
            {
                @stream_set_timeout($infile, $this->class->registry_config['remote_timeout']);
            }

            while (!@feof ($infile))
            {
                @fwrite($outfile, @fread ($infile, 4096));
            }
            @fclose($outfile);
            @fclose($infile);

            // If we aren't using imagemagick let's update our parameters
            //   for the image class handler
            if ($this->class->registry_config['thumbnail_type'] != 'imagemagick')
            {
                $image->in_file_dir = $this->ipsclass->vars['upload_dir'];
                $image->in_file_name = $tmp_file_name;
            }

        }

        // Are we using imagemagick? Let's create the thumb here
        if ( ($this->class->registry_config['thumbnail_type'] == 'imagemagick') and
             (file_exists(preg_replace("/\"/", "",$this->class->registry_config['convert_path']))) )
        {
            // ImageMagick's source/dest filenames
            $source = $this->ipsclass->vars['upload_dir']."/".$tmp_file_name;
            $dest = $this->ipsclass->vars['upload_dir']."/".$thumb_file_name;

            // We are using escapeshellcmd to prevent naughty use
            $cmd = escapeshellcmd($this->class->registry_config['convert_path'] . ' ' . $this->class->registry_config['convert_options'] .' -scale '. $this->class->registry_config['thumbnail_resolution'] .'x'. $this->class->registry_config['thumbnail_resolution'] ." $source $dest");

            // Create our pretty thumb
            exec($cmd);

            // Chmod it, because, because... I feel like it, lol
            @chmod( $dest, 0777 );

            // If we are dealing with a remote image...
            if ( preg_match( "/^http:\/\//i", $data['attach_location'] ) )
            {
                // Remove our temporary file!
                @unlink( $source );

                // If imagemagick failed for some reason set thumb to 'remote'
                if ( ! file_exists($dest) )
                {
                    $return['thumb_location'] = 'remote';

                    return $return;
                }
            }

            // Get our real thumbnail size
            $img_size = @GetImageSize( $dest );

            // Setup the return values
            $return['thumb_width']    = $img_size[0];
            $return['thumb_height']   = $img_size[1];
            $return['thumb_location'] = $thumb_file_name;

            return $return;
        }
        else
        {
            // If not using imagemagick we will fall back on built in
            //   IPB class handler
            $return = $image->generate_thumbnail();

            // We need to do a cleanup check, if we are uploading a remote image
            //   and we are not using imagemagick we need to delete the source
            //   temp file that was downloaded, if it isn't still being used!
            if ( ($this->class->registry_config['thumbnail_type'] != 'imagemagick') &&
                 preg_match( "/^http:\/\//i", $data['attach_location'] ) &&
                 ( $image->in_file_name != $return['thumb_location'] ) )
            {
                // Remove our temporary file!
                @unlink( $this->ipsclass->vars['upload_dir']."/".$tmp_file_name );
            }

            return $return;
        }
    }


    /**
     * Retrieve upload error(s)
     *
     * @return string|empty Error message string or empty.
     */
    function error_msg()
    {
    	return $this->error_msg;
    }


    /**
     * Process upload
     *
     * File uploads with support for thumbnail generation and remote images.
     *
     * @param string $type Specify 'garage', 'registry_mod', or 'registry_gallery'
     * @param integer $id MySQL id for the parent type
     * @return array Array with keys 'attach_ext', 'attach_file', 'attach_location', 'attach_thumb_location', 'attach_hits', 'attach_date', 'attach_filesize'
     */
    function process_upload($type, $id=0)
    {
        //-------------------------------------------------
        // Got attachment types?
        //-------------------------------------------------

        if ( ! is_array( $this->ipsclass->cache['attachtypes'] ) )
        {
            $this->ipsclass->cache['attachtypes'] = array();

            $this->ipsclass->DB->simple_construct( array( 'select' => 'atype_extension,atype_mimetype,atype_post,atype_photo,atype_img', 'from' => 'attachments_type', 'where' => "atype_photo=1 OR atype_post=1" ) );
            $this->ipsclass->DB->simple_exec();

            while ( $r = $this->ipsclass->DB->fetch_row() )
            {
                $this->ipsclass->cache['attachtypes'][ $r['atype_extension'] ] = $r;
            }
        }

        //-------------------------------------------------
        // Set up array
        //-------------------------------------------------

        $attach_data = array(
                              'attach_ext'            => "",
                              'attach_file'           => "",
                              'attach_location'       => "",
                              'attach_thumb_location' => "",
                              'attach_hits'           => 0,
                              'attach_date'           => time(),
                              'attach_filesize'       => 0
                            );

        if ( $type == 'registry_item' )
        {
            if ( ($this->class->registry_config['allow_image_upload'] != 1) and
                 ($this->class->registry_config['max_image_kbytes'] < 1) and
                 ($this->ipsclass->check_perms($this->class->registry_config['upload_perms']) != TRUE) ) return $attach_data;
        }
        elseif ( $type == 'registry_mod' )
        {
            if ( ($this->class->registry_config['allow_mod_image'] != 1) and
                 ($this->class->registry_config['max_image_kbytes'] < 1) and
                 ($this->ipsclass->check_perms($this->class->registry_config['upload_perms']) != TRUE) ) return $attach_data;
        }
        elseif ( $type == 'registry_gallery' )
        {
            if ( ($this->class->registry_config['allow_image_upload'] != 1) and
                 ($this->class->registry_config['max_image_kbytes'] < 1) and
                 ($this->ipsclass->check_perms($this->class->registry_config['upload_perms']) != TRUE) ) return $attach_data;
        }
        else
        {
            // If we do not have a valid 'type', stop the show here
            return $attach_data;
        }

        //-----------------------------------
        // Check to make sure we don't just have
        // http:// in the URL box..
        //------------------------------------
        if ( preg_match( "/^http:\/\/$/i", $this->ipsclass->input['url_image'] ) )
        {
            $this->ipsclass->input['url_image'] = "";
        }

        if ( (empty($this->ipsclass->input['url_image']) == FALSE) AND
             ( $HTTP_POST_FILES['FILE_UPLOAD']['name'] == "" or
              !$HTTP_POST_FILES['FILE_UPLOAD']['name'] or
              ($HTTP_POST_FILES['FILE_UPLOAD']['name'] == "none") )
           )
        {
            // We have a remote image!
            $this->ipsclass->input['url_image'] = trim($this->ipsclass->input['url_image']);

            // Is it dynamic, and are we allowed to use them?
            if ( empty($this->ipsclass->vars['allow_dynamic_img']) )
            {
                if ( preg_match( "/[?&;]/", $this->ipsclass->input['url_image'] ) )
                {
                    $this->error_msg = $this->ipsclass->lang['invalid_image_url'];
                    return $attach_data;
                }
            }

            //-------------------------------------------------
            // Check extension
            //-------------------------------------------------

            $checked = 0;
            $url_image_ext = strtolower( preg_replace( "/^.*\.(\S+)$/", "\\1", $this->ipsclass->input['url_image'] ) );
            $url_image_name =  preg_replace( "/^.*\/(.*\.\S+)$/", "\\1", $this->ipsclass->input['url_image'] );

            if ($this->ipsclass->cache['attachtypes'][$url_image_ext]['atype_mimetype'] != "")
            {
                $checked = 1;
                $attach_data['attach_location'] = $this->ipsclass->input['url_image'];
                $attach_data['attach_ext'] = $url_image_ext;
                $attach_data['attach_file'] = $url_image_name;
            }

            if ($checked != 1)
            {
                $this->error_msg = $this->ipsclass->lang['invalid_image_ext'];
                return $attach_data;
            }

            // Now we need to create a thumbnail for this remote
            //   image, we can only do this if we support real
            //   thumbnails.
            $thumb_data = $this->create_thumbnail( $attach_data, $type, $id );

            // Did it succeed?
            if ( empty($thumb_data['thumb_location']) OR
                 !file_exists($this->ipsclass->vars['upload_dir']."/".$thumb_data['thumb_location']) )
            {
                $this->error_msg = $this->ipsclass->lang['invalid_image_url'];
                return $attach_data;
            }

            $attach_data['attach_thumb_width']    = $thumb_data['thumb_width'];
            $attach_data['attach_thumb_height']   = $thumb_data['thumb_height'];
            $attach_data['attach_thumb_location'] = $thumb_data['thumb_location'];
            $attach_data['attach_is_image']       = 1;

            if ( $attach_data['attach_location'] == $attach_data['attach_thumb_location'] )
            {
                // If we don't have imagemagick/gd2/gd1 we will use this tag
                //   to tell later on that it's a remote image with
                //   no thumbnail :(
                $attach_data['attach_thumb_location'] = 'remote';
            }

            $this->ipsclass->DB->do_insert( 'registry_images', $attach_data );

            return $this->ipsclass->DB->get_insert_id();
        }
        else
        {
            // Initialize class_upload
            $upload = new class_upload();

            //-------------------------------------------------
            // Set up the variables
            //-------------------------------------------------

            $upload->out_file_name    = $type ."-$id-". time();
            $upload->out_file_dir     = $this->ipsclass->vars['upload_dir'];
            $upload->max_file_size    = $this->class->registry_config['max_image_kbytes'] * 1024;
            $upload->make_script_safe = 1;
            $upload->force_data_ext   = 'ipb';

            //-------------------------------------------------
            // Populate allowed extensions
            //-------------------------------------------------

            if ( is_array( $this->ipsclass->cache['attachtypes'] ) and count( $this->ipsclass->cache['attachtypes'] ) )
            {
                foreach( $this->ipsclass->cache['attachtypes'] as $idx => $data )
                {
                    if ( $data['atype_post'] )
                    {
                        $upload->allowed_file_ext[] = $data['atype_extension'];
                    }
                }
            }

            //-------------------------------------------------
            // Upload...
            //-------------------------------------------------

            $upload->upload_process();

            //-------------------------------------------------
            // Error?
            //-------------------------------------------------

            if ( $upload->error_no )
            {
                switch( $upload->error_no )
                {
                    case 1:
                        // No upload
                        return $attach_data;
                    case 2:
                        // Invalid file ext
                        $this->error_msg = 'invalid_mime_type';
                        return $attach_data;
                    case 3:
                        // Too big...
                        $this->ipsclass->Error( array( LEVEL => 1, MSG => 'upload_to_big') );
                    case 4:
                        // Cannot move uploaded file
                        $this->error_msg = 'upload_failed';
                        return $attach_data;
                }
            }

            //-------------------------------------------------
            // Check the file resolution size
            //-------------------------------------------------

            $img_size = array();

            $img_size = @GetImageSize( $upload->saved_upload_name );

            if ( ( $img_size[0] > $this->class->registry_config['max_image_resolution'] ) OR
                 ( $img_size[1] > $this->class->registry_config['max_image_resolution'] ) )
            {
                // Ut oh, too big, delete it and report to user
                if ( @file_exists( $upload->saved_upload_name ) )
                {
                    @unlink( $upload->saved_upload_name );
                }

                $this->error_msg = $this->ipsclass->lang['upload_resolution_to_big'];

                return $attach_data;
            }

            //-------------------------------------------------
            // Still here?
            //-------------------------------------------------

            if ( $upload->saved_upload_name and @file_exists( $upload->saved_upload_name ) )
            {
                $attach_data['attach_filesize'] = @filesize( $upload->saved_upload_name  );
                $attach_data['attach_location'] = $upload->parsed_file_name;
                $attach_data['attach_file']     = $upload->original_file_name;
                $attach_data['attach_is_image'] = $upload->is_image;
                $attach_data['attach_ext']      = $upload->real_file_extension;

                if ( $attach_data['attach_is_image'] == 1 )
                {
                    $thumb_data = $this->create_thumbnail( $attach_data, $type, $id );

                    $attach_data['attach_thumb_width']    = $thumb_data['thumb_width'];
                    $attach_data['attach_thumb_height']   = $thumb_data['thumb_height'];
                    $attach_data['attach_thumb_location'] = $thumb_data['thumb_location'];
                }

                $this->ipsclass->DB->do_insert( 'registry_images', $attach_data );

                return $this->ipsclass->DB->get_insert_id();
            }
        }

        // If we are still here, something really bad happened :(
        return $attach_data;
    }


    /**
     * Verify a remote file exists
     *
     * This is kind of tricky, this verifies against the response code
     * from trying to get to the remote file. Basically if we don't get
     * response code 200 we will assume something is wrong and return
     * false. This code does take into account redirect responses and
     * will follow the redirect to verify that location.
     *
     * @param string $url Remote file to verify
     * @todo Eliminate duplicate function {@link ad_registry_tools::remote_file_exists()}
     * @return boolean TRUE|FALSE
     */
    function remote_file_exists($url)
    {
        // Make sure php will allow us to do this...
        if ( ini_get('allow_url_fopen') )
        {
            $url_p = parse_url($url);

            if ( isset($url_p['host']) )
            {
                $host = $url_p['host'];
            }
            else
            {
               return false;
            }

            $fp = @fsockopen($host, 80, $errno, $errstr, 20);
            if (!$fp)
            {
               return false;
            }
            else
            {
               $parse = parse_url($url);
               $host = $parse['host'];

               @fputs($fp, 'HEAD '.$url." HTTP/1.1\r\n");
               @fputs($fp, 'HOST: '.$host."\r\n");
               @fputs($fp, "Connection: close\r\n\r\n");
               $headers = '';
               while ( !@feof ($fp) )
               {
                   $headers .= @fgets ($fp, 128);
               }
            }
            @fclose ($fp);

            $arr_headers = explode("\n", $headers);
            if ( isset($arr_headers[0]) )
            {
               if( strpos($arr_headers[0], '200') !== false )
               {
                   return true;
               }
               if( ( strpos($arr_headers[0], '404') !== false ) ||
                   ( strpos($arr_headers[0], '509') !== false ) ||
                   ( strpos($arr_headers[0], '410') !== false ) )
               {
                   return false;
               }
               if( ( strpos($arr_headers[0], '301') !== false ) ||
                   ( strpos($arr_headers[0], '302') !== false ) )
               {
                   $matches = array();

                   preg_match("/Location:\s*(.+)\r/i", $headers, $matches);

                   if ( !isset($matches[1]) )
                   {
                       return false;
                   }
                   $nextloc = $matches[1];
                   return $this->remote_file_exists($nextloc);
               }
            }

            // If we are still here then we got an unexpected header
            return false;
        }
        else
        {
            // Since we aren't allowed to use URL's bomb out
            return false;
        }
    }

}

?>